<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Exception\Handler;

use App\Abstract\Exception;
use App\Api\Constants\Business;
use App\Helper\RespHelper;
use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use Hyperf\Context\Context;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Validation\ValidationException;
use Psr\Http\Message\ResponseInterface;
use Throwable;

use function Hyperf\Config\config;

class AppExceptionHandler extends Exception
{

    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        // 阻止异常冒泡
        $this->stopPropagation();
        $uri = $this->request->getRequestUri();
        $isControl = str_starts_with($uri, URL_PREFIX_CONTROL);
        if ($isControl) {
            $logGroup = 'http-back';
        } else {
            $logGroup = 'http-apis';
        }
        // 设定日志模块
        $this->setLogger($logGroup);

        $message = sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile());

        // 判断是否由业务异常类抛出的异常
        if ($throwable instanceof BusinessException) {
            // 业务逻辑错误日志处理
            if (config('app_log_enable')) {
                $this->logger->warning(print_r(['url' => $uri, 'msg' => $message], true));
            }
            $data = Context::get(Business::EXCEPTION_RESPDATA);
            $resp = RespHelper::back($throwable->getCode(), $throwable->getMessage(), $data);
            if ($data) Context::destroy(Business::EXCEPTION_RESPDATA);
            return $this->response->withStatus(200)->json($resp);
        }

        //判断是否是验证器的
        if ($throwable instanceof ValidationException) {
            // 阻止异常冒泡
            $this->stopPropagation();
            $body = $throwable->validator->errors()->first();
            if (config('app_log_enable')) {
                $this->logger->warning(print_r(['url' => $uri, 'msg' => $body], true));
            }
            if ($isControl) {
                $resp = RespHelper::back(ErrorCode::ERR_PARAMETER, $body);
            } else {
                $resp = RespHelper::fail($body);
            }
            return $this->response->withStatus(200)->json($resp);
        }

        try {
            $statusCode = $throwable->getStatusCode();
        } catch (\Throwable $exception) {
            $statusCode = 500;
        }

        $requestMethod = $this->request->getMethod();
        // OPTIONS 请求处理
        if ($requestMethod === 'OPTIONS') {
            return $this->response->withStatus(200);
        }
        $schema = $requestMethod . "[$statusCode]:" . $this->request->url();
        $errorMessage = $this->request->getPathInfo() . " ›› {$statusCode}";
        $errCode = $throwable->getCode();
        $errorMsg = $throwable->getMessage();
        $this->logger->error($errorMessage . PHP_EOL . print_r([
                'schema' => $schema,
                'code' => $errCode,
                'file' => $throwable->getFile() . ":" . $throwable->getLine(),
                'error' => $errorMsg,
                'header' => formatLogArray($this->request->getHeaders()),
                'query' => formatLogArray($this->request->getQueryParams()),
                'body' => json_encode($this->request->getParsedBody(), JSON_UNESCAPED_UNICODE),
            ], true));
        !IS_PROD && stdPrinter()->error("{$schema}：$errorMsg");
        if ($isControl) {
            $resp = RespHelper::back(ErrorCode::ERR_SERVER, $errorMsg);
        } else {
            $resp = RespHelper::back(ErrorCode::ERR_SERVER, $errorMsg);
        }
        return $this->response->withStatus($statusCode)->withBody(
            new SwooleStream(json_encode($resp, JSON_UNESCAPED_UNICODE))
        );
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}
